package com.ruoyi.chatroom.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.ruoyi.chatroom.domain.SendMessageEntity;
import com.ruoyi.chatroom.domain.WebSocketClient;
import com.ruoyi.common.constant.MessageType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhutianyu
 * @date 2021/8/26 16:20
 */
@ServerEndpoint(value = "/pirated/wechat/{userName}")
@Component
@Slf4j
public class WebSocketService {
    /**
     * 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
     */
    private static int onlineCount = 0;
    /**
     * concurrent包的线程安全Set，用来存放每个客户端对应的WebSocketServer对象。
     */
    private static ConcurrentHashMap<String, WebSocketClient> webSocketMap = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 接收userName
     */
    private String userName;

    /**
     * 用户上线
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userName") String userName) {
        log.info("用户:" + userName + ",开始连接,session:" + session);
        if (!webSocketMap.containsKey(userName)) {
            addOnlineCount(); // 在线数 +1
            this.session = session;
            this.userName = userName;
            WebSocketClient client = new WebSocketClient();
            client.setSession(session);
            client.setUri(session.getRequestURI().toString());
            webSocketMap.put(userName, client);

            //建立连接成功,通知所有在线的用户
            sendMessageAll(userName, MessageType.NOTIFY_ONLINE.getValue());
        }
    }

    private void sendMessageAll(String userName, String messageType) {
        synchronized (session) {
            StringJoiner sj = new StringJoiner(",");
            webSocketMap.forEach((key, value) -> sj.add(key));

            Map<String, String> map = Maps.newHashMap();
            //消息类型
            map.put("messageType", messageType);
            //在线人数
            map.put("onlineCount", Integer.toString(onlineCount + 9));
            //用户集合
            map.put("userNames", sj.toString());
            //新上线或下线的用户
            map.put("online", userName);
            webSocketMap.forEach((key, value) -> {
                try {
                    value.getSession().getBasicRemote().sendText(JSONObject.toJSONString(map));
                } catch (IOException e) {
                    log.error("发送消息异常,参数为:{},异常信息为:{}", JSONObject.toJSONString(map), e);
                }
            });
        }
    }

    /**
     * 发送消息
     */
    public void sendMessage(SendMessageEntity entity) {
        synchronized (entity) {
            String receiveUser = entity.getReceiveUser();
            if (webSocketMap.containsKey(receiveUser)) {
                WebSocketClient socketClient = webSocketMap.get(receiveUser);
                entity.setMessageType(MessageType.MESSAGE.getValue());
                try {
                    socketClient.getSession().getBasicRemote().sendText(JSON.toJSONString(entity));
                } catch (IOException e) {
                    log.error("发送消息异常,参数为:{},异常信息为:{}", JSONObject.toJSONString(entity), e);
                }
            }
        }
    }

    /**
     * 用户下线
     */
    @OnClose
    public void onClose() {
        if (webSocketMap.containsKey(userName)) {
            webSocketMap.remove(userName);
            if (webSocketMap.size() > 0) {
                subOnlineCount();
                //有用户下线,通知所有在线的用户
                sendMessageAll(userName, MessageType.NOTIFY_OFFLINE.getValue());
            }
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        //刷新在线人数和在线用户
        if (StringUtils.equals(message, "refresh")) {
            refresh(session, MessageType.NOTIFY_ONLINE.getValue());
        }
    }

    private void refresh(Session session, String messageType) {
        synchronized (session) {
            StringJoiner sj = new StringJoiner(",");
            webSocketMap.forEach((key, value) -> sj.add(key));

            Map<String, String> map = Maps.newHashMap();
            //消息类型
            map.put("messageType", messageType);
            //在线人数
            map.put("onlineCount", Integer.toString(onlineCount + 9));
            //用户集合
            map.put("userNames", sj.toString());
            try {
                session.getBasicRemote().sendText(JSONObject.toJSONString(map));
            } catch (IOException e) {
                log.error("发送消息异常,参数为:{},异常信息为:{}", JSONObject.toJSONString(map), e);
            }
        }

    }

    /**
     * 发送单个消息
     */
    public void sendOneMessage(Session session, String messageType, String userName) {
        synchronized (session) {
            StringJoiner sj = new StringJoiner(",");
            webSocketMap.forEach((key, value) -> sj.add(key));

            Map<String, String> map = Maps.newHashMap();
            //消息类型
            map.put("messageType", messageType);
            //在线人数
            map.put("onlineCount", Integer.toString(onlineCount + 9));
            //用户集合
            map.put("userNames", sj.toString());
            //新上线或下线的用户
            map.put("online", userName);
            try {
                session.getBasicRemote().sendText(JSONObject.toJSONString(map));
            } catch (IOException e) {
                log.error("发送消息异常,参数为:{},异常信息为:{}", JSONObject.toJSONString(map), e);
            }
        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误:" + this.userName + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 连接服务器成功后主动推送
     */
    public void sendMessage(String message) throws IOException {
        synchronized (session) {
            this.session.getBasicRemote().sendText(message);
        }
    }

    /**
     * 向指定客户端发送消息
     *
     * @param userName
     * @param message
     */
    public static void sendMessage(String userName, String message) {
        webSocketMap.forEach((key, value) -> log.info("map中的数据为:{},{}", key, value));
        try {
            //根据消息长度休眠
            WebSocketClient webSocketClient = webSocketMap.get(userName);
            if (webSocketClient != null) {
                log.info("休眠" + message.length() + "秒开始");
                try {
                    int i = message.length() * 1000;
                    if (i != 0) {
                        Thread.sleep(i);
                    }
                } catch (InterruptedException e) {
                }
                log.info("休眠" + message.length() + "秒结束");
                StringBuilder sb = new StringBuilder(message);
                webSocketClient.getSession().getBasicRemote().sendText(sb.reverse().toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }


    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        onlineCount--;
    }

    public static void setOnlineCount(int onlineCount) {
        WebSocketService.onlineCount = onlineCount;
    }


    public static ConcurrentHashMap<String, WebSocketClient> getWebSocketMap() {
        return webSocketMap;
    }

    public static void setWebSocketMap(ConcurrentHashMap<String, WebSocketClient> webSocketMap) {
        WebSocketService.webSocketMap = webSocketMap;
    }

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public String getuserName() {
        return userName;
    }

    public void setuserName(String userName) {
        this.userName = userName;
    }
}
